//derived from http://www.graphviz.org/doc/info/lang.html

grammar DOT;

graph           :   STRICT? (GRAPH | DIGRAPH) id? '{' stmt_list '}' ;
stmt_list       :   (stmt ';'?)*;
stmt            :   node_stmt
                |   edge_stmt
                |   attr_stmt
                |   id '=' id
                |   subgraph
                ;

attr_stmt       :   (GRAPH | NODE | EDGE) attr_list ;
attr_list       :   ('[' a_list? ']')+ ;
a_list          :   (id ('=' id)? ','?)+ ;
edge_stmt       :   (node_id | subgraph) edgeRHS attr_list? ;
edgeRHS         :   (edgeop (node_id | subgraph))+ ;
edgeop          :   '->' | '--' ;
node_stmt       :   node_id attr_list? ;
node_id         :   id port? ;
port            :   ':' id (':' id)? ;
subgraph        :   (SUBGRAPH id?)? '{' stmt_list '}' ;
id              :   ID
                |   STRING
                |   HTML_STRING
                |   NUMBER
                ;

//the keywords node, edge, graph, digraph, subgraph, and strict are case-independent
STRICT          :   [Ss][Tt][Rr][Ii][Cc][Tt] ;
GRAPH           :   [Gg][Rr][Aa][Pp][Hh] ;
DIGRAPH         :   [Dd][Ii][Gg][Rr][Pp][Hh] ;
NODE            :   [Nn][Oo][Dd][Ee] ;
EDGE            :   [Ee][Dd][Gg][Ed] ;
SUBGRAPH        :   [Ss][Uu][Bb][Gg][Rr][Aa][Pp][Hh] ;

//a numberal [-]?(.[0-9]+ | [0-9]+(.[0-9]*)?)
NUMBER          :   '-'? ('.' DIGIT+ | DIGIT+ ('.' DIGIT*)?) ;
fragment
DIGIT           :   [0-9] ;

//any double-quoted string ("....") possibly containing escaped quotes
STRING          :   '"' ('\\"' | .)*? '"' ;

//any string of alphabetic ([a-zA-Z\200-\377]) characters, underscores '-' or digits [0-9], not beginning with a digit
ID              :   LETTER (LETTER | DIGIT)* ;
fragment
LETTER          :   [a-zA-Z\u0080-\u00FF_] ;

//HTML strings, angle brackets must occur in matched pairs, and unescaped newlines are allowed
HTML_STRING     :   '<' (TAG | ~[<>])* '>' ;
fragment
TAG             :   '<' .*? '>' ;

COMMENT         :   '/*' .*? '*/'           -> skip ;
LINE_COMMENT    :   '//' .*? '\r'? '\n'     -> skip ;


PREPROC         :   '#' .*? '\n'    -> skip ;
WS              :   [ \t\n\r]+      -> skip ;